package peace.gui.controls.scroll
{

import flash.display.InteractiveObject;
import flash.events.Event;
import flash.events.MouseEvent;
import flash.geom.Point;

import peace.data.Interval;
import peace.gui.controls.Control;
import peace.gui.controls.buttons.PushButton;
/**
* 
*/
[Event(name="scroll", type="peace.gui.controls.scroll.ScrollEvent")]
/**
* 
*/
[Event(name="scrollEnd", type="peace.gui.controls.scroll.ScrollEvent")]
/**
 * 
 */
public class Slider extends Control
{
	/**
	 * 
	 */
	protected var _value:Number;
	/**
	 * 
	 */	
	protected var _interval:Interval;
	/**
	 * 
	 */	
	protected var _size:Number = 200;
	/**
	 * 
	 */	
	protected var _step:Number = 0;
	/**
	 * 
	 */	
	protected var delta:Number;
	
	
	protected var runnerDragged:Boolean = false;
	protected var runnerDragPoint:Point = new Point();

	protected var skin:Skin=null;
	
	public function Slider (interval:Interval = null, 
							runnerSkin:InteractiveObject=null, trackSkin:InteractiveObject=null)
	{
		_interval = interval || new Interval(0, 1);
		
		_value = _interval.min;
				
		setSkin(runnerSkin, trackSkin);
		
		resize();		

		addEventListener(Event.ADDED_TO_STAGE, onAddedToStage, false, 0, true);
	}
	
	protected function onAddedToStage (e:Event=null):void
	{
		stage.addEventListener(MouseEvent.MOUSE_MOVE, onRunner, false, 0, true);
		stage.addEventListener(MouseEvent.MOUSE_UP, onRunner, false, 0, true);
	}
	/**
	 * 
	 * @param i Interval
	 * 
	 */	
	public function set interval (i:Interval):void
	{
		_interval = i;
		resize();
	}
	/**
	 * 
	 * @return Interval
	 * 
	 */	
	public function get interval ():Interval
	{
		return _interval;
	}
	/**
	 * 
	 * @param n
	 * 
	 */
	override public function set value(v:*):void
	{
		scrollTo(Number(v), false);
	}
	/**
	 * 
	 * @return 
	 * 
	 */	
	override public function get value():*
	{
		return _value;
	}
	/**
	 * Задаем ширину(высоту) в пикселях (в зависимости от направлености) 
	 * @param n
	 * 
	 */
	public function set size (n:Number):void
	{
		_size = n;
		resize();
	}
	/**
	 * 
	 * @return 
	 * 
	 */	
	public function get size ():Number
	{
		return _size;
	}
	/**
	 * Задаем шаг возможных дискретных значений слайдера
	 * 
	 * @param n
	 * 
	 */	
	public function set step (n:Number):void
	{
		_step = new Interval(0, _interval.length).crop(n);
		
		// Ничего не делаем
		if (_step == 0) return;
		
		scrollTo (_value, true);
	}
	/**
	 * 
	 * @return 
	 * 
	 */	
	public function get step ():Number
	{
		return _step;
	}
	
	/**
	 * 
	 * 
	 */	
	protected function resize ():void
	{
		skin.track.width = _size;
		
		delta = (skin.track.width-skin.runner.width) / _interval.length;
		
		placeRunnerTo(_value);
	}
	/**
	 * 
	 * @param n
	 * @param raiseEvent
	 * 
	 */
	protected function scrollTo (n:Number, raiseEvent:Boolean=true):void
	{
		n = _interval.crop(n);
		
		if (_step > 0)
		{
			n = _interval.min + Math.round((n - _interval.min)/_step) * _step;
		}

		if (n == _value)
		{
			return;
		}
		
		_value = n;
		placeRunnerTo(n);
		
		if (raiseEvent)
		{
			dispatchEvent(new ScrollEvent(ScrollEvent.SCROLL, _value));
		}
	}
	/**
	 * 
	 * @param n
	 * 
	 */	
	protected function placeRunnerTo (n:Number):void
	{
		skin.runner.x = skin.track.x + delta * (n - _interval.min);
	}
	
	/**
	 * Обработчики событий мышки
	 */	
	private function onTrack (e:MouseEvent):void
	{
		if (e.type == MouseEvent.MOUSE_DOWN)
		{
			var value:Number = skin.track.mouseX - (skin.runner.width / 2);
			scrollTo (_interval.min + value / delta);
		}
	}
	
	private function onRunner (e:MouseEvent):void
	{
		if (e.type == MouseEvent.MOUSE_DOWN)
		{
			runnerDragPoint.x = skin.runner.mouseX;
			runnerDragPoint.y = skin.runner.mouseY;
			runnerDragged = true;
		}
		else if (e.type == MouseEvent.MOUSE_UP)
		{
			runnerDragged = false;
			
			dispatchEvent(new ScrollEvent(ScrollEvent.SCROLL_END, _value));
		}
		else if (e.type == MouseEvent.MOUSE_MOVE && runnerDragged)
		{
			var value:Number = skin.track.mouseX - runnerDragPoint.x;
			scrollTo (_interval.min + value/delta);
			e.updateAfterEvent();
		}
	}
	
	public function setSkin (runner:InteractiveObject, track:InteractiveObject):void
	{
		if (skin && contains(skin.runner))
		{
			removeChild(skin.runner);
			skin.runner.removeEventListener(MouseEvent.MOUSE_DOWN, onRunner);
		}
		
		if (skin && contains(skin.track))
		{
			removeChild(skin.track);
			skin.track.removeEventListener(MouseEvent.MOUSE_DOWN, onTrack);
		}
		// оборачиваем в PushButton чтобы не учитывать скейлинг
		if (track && !(track is PushButton))
		{
			track = new PushButton(track);
		}

		skin = new Skin(runner, track);

		addChild(skin.track);
		skin.track.addEventListener(MouseEvent.MOUSE_DOWN, onTrack, false, 0, true);
		addChild(skin.runner);
		skin.runner.addEventListener(MouseEvent.MOUSE_DOWN, onRunner, false, 0, true);
		
		skin.runner.y = (skin.track.height - skin.runner.height) / 2;

		skin.track.width = _size;
		delta = (skin.track.width - skin.runner.width) / _interval.length;
		placeRunnerTo(_value);
	}								
	
}

}

	import peace.gui.controls.buttons.PushButton;
	import flash.display.InteractiveObject;
	import flash.display.Shape;
	import flash.display.Graphics;
	import flash.display.LineScaleMode;
	

internal class Skin
{
	private static const DEFAULT_HEIGHT:uint = 10;
	
	private var defaultRunnerSkin:PushButton=null;
	private var defaultTrackSkin:PushButton=null;
	
	private var _runner:InteractiveObject;
	private var _track:InteractiveObject;
	
	public function get runner():InteractiveObject
	{
		return (_runner==null) ? getDefaultRunnerSkin() : _runner;	
	}
	public function get track():InteractiveObject
	{
		return (_track==null) ? getDefaultTrackSkin() : _track;	
	}
	
	public function Skin (runner:InteractiveObject=null, track:InteractiveObject=null)
	{
		_runner = runner;
		_track = track;
	}
	
	private function getDefaultRunnerSkin ():InteractiveObject
	{
		if (defaultRunnerSkin==null)
		{
			var g:Shape = new Shape();
			g.graphics.lineStyle(0, 0);
			g.graphics.beginFill(0xcccccc);
			g.graphics.drawRect(0,0,5,DEFAULT_HEIGHT);
			defaultRunnerSkin = new PushButton(g);
		}
		return defaultRunnerSkin;
	}
	
	private function getDefaultTrackSkin ():InteractiveObject
	{
		if (defaultTrackSkin==null)
		{
			var g:Shape = new Shape();
			g.graphics.lineStyle(0,0,0);
			g.graphics.beginFill(0,0);
			g.graphics.drawRect(0,0,5,DEFAULT_HEIGHT);
			g.graphics.lineStyle(0,0x666666,1,true, LineScaleMode.HORIZONTAL);
			g.graphics.moveTo(0,DEFAULT_HEIGHT/2);
			g.graphics.lineTo(5, DEFAULT_HEIGHT/2);
			defaultTrackSkin = new PushButton(g);
		}
		return defaultTrackSkin;
	}
}